import json
import random
import re
from datetime import datetime

from info.libs.yuntongxun.sms import CCP
from info.models import User
from info.utils.response_code import RET

from flask import request, current_app, make_response, jsonify, session

from info import redis_store, constants, db
from info.utils.captcha.captcha import captcha

from . import passport_blue


@passport_blue.route("/image_code")
def get_image_code():
    """
    图片验证码逻辑:
    1.前端页面随机生成验证码编号,并将编号提交到后台去请求验证码图片
    2.后台生成图片验证码,并将验证码文字内容当做值,验证码编号当做key存储在redis中
    3.后台把验证码图片当做响应返回给前端
    4.前端申请发送短信验证码的时候带上验证码编号和用户输入的内容
    5.后台取出验证码编号对应的验证码内容与前端传过来的验证码内容作对比
    6.如果一样,则向指定的手机发送验证码,如果不一样,返回验证码错误


    生成验证码
    删除之前验证码并保存当前验证码
    错误处理
    响应返回
    :return:
    """
    # 1.获取图片参数编号
    image_code_id = request.args.get("imageCodeId", None)
    # 2.生成验证码
    name, text, image = captcha.generate_captcha()
    current_app.logger.debug(text)
    # 3.保存当前验证码
    try:
        redis_store.set("ImageCodeId_" + image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        # 将错误信息写入log日志文件
        current_app.logger.error(e)
        return make_response(jsonify(errno=RET.DATAERR, errmsg="保存图片验证码失败"))
    # 4.响应返回
    response = make_response(image)
    # 设置响应头
    response.headers["Content-Type"] = 'image/jpg'

    return response


@passport_blue.route("/sms_code", methods=["POST"])
def send_sms_code():
    """发送短信
    1. 接收参数并判断是否有值
    2. 校验手机号是正确
    3. 通过传入的图片编码去redis中查询真实的图片验证码内容
    4. 进行验证码内容的比对
    5. 生成发送短信的内容并发送短信
    6. redis中保存短信验证码内容
    7. 返回发送成功的响应
    """
    # 1. 接收参数并判断是否有值
    # parms_dict = json.loads(request.data)
    parms_dict = request.json
    mobile = parms_dict["mobile"]
    image_code = parms_dict["image_code"]
    image_code_id = parms_dict["image_code_id"]

    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不正确")
    # 2. 校验手机号是否正确
    if not re.match("^1[35786][0-9]{9}$", mobile):
        return jsonify(errno=RET.DATAERR, errmsg="手机号码不正确")
    # 3. 从redis中获取数据
    try:
        real_image_code = redis_store.get("ImageCodeId_" + image_code_id)
    except Exception as e:
        # 写入日志
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据系统故障")
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg="验证码已经过期")
    # 对比验证码
    if real_image_code.upper() != image_code.upper():
        return jsonify(errno=RET.DATAERR, errmsg="图形验证码不正确")
    # 生成短信验证码
    sms_code_str = "%06d" % random.randint(0, 999999)
    current_app.logger.debug("短信验证码的内容是:%s" % sms_code_str)
    # 请求第三方,发送验证码
    result = CCP().send_template_sms(mobile, [sms_code_str, constants.SMS_CODE_REDIS_EXPIRES // 60], "1")
    # 判断验证码是否发送成功
    if result != 0:
        return jsonify(errno=RET.THIRDERR, errmsg="发送验证码失败")
    # 保存验证码到redis
    try:
        redis_store.set("sms_" + mobile, sms_code_str, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据系统故障")
    # 返回成功响应
    return jsonify(errno=RET.OK, errmsg="验证码已发送")


@passport_blue.route('/register', methods=["POST"])
def register():
    """
        注册的逻辑
        1. 获取参数
        2. 校验参数
        3. 取到服务器保存的真实的短信验证码内容
        4. 校验用户输入的短信验证码内容和真实验证码内容是否一致
        5. 如果一致，初始化 User 模型，并且赋值属性
        6. 将 user 模型添加数据库
        7. 返回响应
        :return:
        """
    # 1. 获取参数
    param_dict = request.json
    mobile = param_dict.get("mobile")
    smscode = param_dict.get("smscode")
    password = param_dict.get("password")
    # 2. 校验参数
    if not all([mobile, smscode, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    if not re.match("^1[35678]\\d{9}$", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式不正确")
    # 3. 取到服务器保存的真实的短信验证码内容
    try:
        real_sms_code = redis_store.get("sms_" + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库查询错误")
    # 4. 校验用户输入的短信验证码内容和真实验证码内容是否一致
    if real_sms_code != smscode:
        return jsonify(errno=RET.NODATA, errmsg="验证码已过期")
    # 5. 如果一致，初始化 User 模型，并且赋值属性
    user = User()
    user.mobile = mobile
    # 暂时没有nick_name, 用mobile代替
    user.nick_name = mobile
    # 记录最后一次登录的时间
    user.last_login = datetime.now()
    # 加密的密码
    # 需求：在设置 password 的时候，去对 password 进行加密，并且将加密结果给 user.password_hash 赋值
    user.password = password
    # 6. 将 user 模型添加数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="数据库保存失败")
    # 7.往session中保存数据表示当前已经登录
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    # 返回响应
    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_blue.route('/login', methods=["POST"])
def login():
    """登录"""
    # 1.获取参数
    params_dict = request.json
    mobile = params_dict.get("mobile")
    password = params_dict.get("password")
    # 2. 校验参数
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    # 3. 校验手机号
    if not re.match("^1[35678]\\d{9}$", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式不正确")
    # 4 . 校验是否存在手机号
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库查询错误")
    # 5. 校验用户是否存在
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="用户不存在")
    # 6. 校验密码是否一致
    if not user.check_passowrd(password):
        return jsonify(errno=RET.PWDERR, errmsg="用户名或密码错误")
    # 7. 保存用户登录状态 注册时候的数据
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    # 8. 设置当前用户最后一次登录时间
    user.last_login = datetime.now()
    # 在视图函数中,对模型身上的属性有修改,需要db.session.commit()保存到数据库
    # 对sqlalchemy进行配置,可以不用自己去commit
    # 9. 返回响应
    return jsonify(errno=RET.OK, errmsg="登录成功")


@passport_blue.route("/logout", methods=["POST"])
def logout():
    """登出"""
    # 1.清除session
    session.pop("user_id", None)
    session.pop("mobile", None)
    session.pop("nick_name", None)
    session.pop("is_admin",None)
    # 2. 返回响应
    return jsonify(errno=RET.OK, errmsg="登出成功")
